#include "amrFileCodec.h"


#pragma mark - Encode

int amrEncodeMode[] = {4750, 5150, 5900, 6700, 7400, 7950, 10200, 12200};
int isRecord = 0;
// 从WAVE文件中跳过WAVE文件头，直接到PCM音频数据

void skipToPCMAudioData(FILE *waveFile) {
    RIFFHEADER riff;
    FMTBLOCK fmt;
    XCHUNKHEADER chunk;
    WAVEFORMATX wfx;
    
    // 1. 读RIFF头
    fread(&riff, 1, sizeof(RIFFHEADER), waveFile);
    
    // 2. 读FMT块 - 如果 fmt.nFmtSize>16 说明需要还有一个附属大小没有读
    fread(&chunk, 1, sizeof(XCHUNKHEADER), waveFile);
    if (chunk.nChunkSize > 16) {
        fread(&wfx, 1, sizeof(WAVEFORMATX), waveFile);
    }
    else {
        memcpy(fmt.chFmtID, chunk.chChunkID, 4);
        fmt.nFmtSize = chunk.nChunkSize;
        fread(&fmt.wf, 1, sizeof(WAVEFORMAT), waveFile);
    }
    
    // 3.转到data块 - 有些还有fact块等。
    while (1) {
        fread(&chunk, 1, sizeof(XCHUNKHEADER), waveFile);
        if (!memcmp(chunk.chChunkID, "data", 4)) {
            break;
        }
        // 因为这个不是data块,就跳过块数据
        fseek(waveFile, chunk.nChunkSize, SEEK_CUR);
    }
}

// 从WAVE文件读一个完整的PCM音频帧
// 返回值: 0-错误 >0: 完整帧大小
size_t ReadPCMFrame(short speech[], FILE *fpwave, size_t nChannels, size_t nBitsPerSample) {
    size_t nRead = 0;
    int x = 0, y = 0;
    //	unsigned short ush1=0, ush2=0, ush=0;
    
    // 原始PCM音频帧数据
    unsigned char pcmFrame_8b1[PCM_FRAME_SIZE];
    unsigned char pcmFrame_8b2[PCM_FRAME_SIZE << 1];
    unsigned short pcmFrame_16b1[PCM_FRAME_SIZE];
    unsigned short pcmFrame_16b2[PCM_FRAME_SIZE << 1];
    
    if (nBitsPerSample == 8 && nChannels == 1) {
        nRead = fread(pcmFrame_8b1, (nBitsPerSample / 8), (size_t)PCM_FRAME_SIZE * nChannels, fpwave);
        for (x = 0; x < PCM_FRAME_SIZE; x++) {
            speech[x] = ((short) pcmFrame_8b1[x] << 7);
        }
    }
    else if (nBitsPerSample == 8 && nChannels == 2) {
        nRead = fread(pcmFrame_8b2, (nBitsPerSample / 8), (size_t)PCM_FRAME_SIZE * nChannels, fpwave);
        for (x = 0, y = 0; y < PCM_FRAME_SIZE; y++, x += 2) {
            // 1 - 取两个声道之左声道
            speech[y] = ((short) pcmFrame_8b2[x + 0] << 7);
            // 2 - 取两个声道之右声道
            //speech[y] =(short)((short)pcmFrame_8b2[x+1] << 7);
            // 3 - 取两个声道的平均值
            //ush1 = (short)pcmFrame_8b2[x+0];
            //ush2 = (short)pcmFrame_8b2[x+1];
            //ush = (ush1 + ush2) >> 1;
            //speech[y] = (short)((short)ush << 7);
        }
    }
    else if (nBitsPerSample == 16 && nChannels == 1) {
        nRead = fread(pcmFrame_16b1, (nBitsPerSample / 8), (size_t)PCM_FRAME_SIZE * nChannels, fpwave);
        for (x = 0; x < PCM_FRAME_SIZE; x++) {
            speech[x] = (short) pcmFrame_16b1[x + 0];
        }
    }
    else if (nBitsPerSample == 16 && nChannels == 2) {
        nRead = fread(pcmFrame_16b2, (nBitsPerSample / 8), (size_t)PCM_FRAME_SIZE * nChannels, fpwave);
        for (x = 0, y = 0; y < PCM_FRAME_SIZE; y++, x += 2) {
            //speech[y] = (short)pcmFrame_16b2[x+0];
            speech[y] = (short) (((int) pcmFrame_16b2[x + 0] + (int) pcmFrame_16b2[x + 1])) >> 1;
        }
    }
    
    // 如果读到的数据不是一个完整的PCM帧, 就返回0
    if (nRead < PCM_FRAME_SIZE * nChannels) return 0;
    
    return nRead;
}

// WAVE音频采样频率是8khz
// 音频样本单元数 = 8000*0.02 = 160 (由采样频率决定)
// 声道数 1 : 160
//        2 : 160*2 = 320
// bps决定样本(sample)大小
// bps = 8 --> 8位 unsigned char
//       16 --> 16位 unsigned short

size_t encodeWAVEFileToAMRFile(const char *pchWAVEFilename, const char *pchAMRFileName, char errorDescription[100]) {
    FILE *waveFile;
    FILE *amrFile;
    size_t channels = 1;
    size_t bitsPerSample = 16;
    /* input speech vector */
    short speech[160];
    
    /* counters */
    size_t frames = 0, bytes = 0;
    size_t byte_counter;
    
    /* pointer to encoder state structure */
    void *enstate;
    
    /* requested mode */
    enum Mode req_mode = MR122;
    int dtx = 0;
    
    /* bit stream file type */
    unsigned char amrFrame[MAX_AMR_FRAME_SIZE];
    
    waveFile = fopen(pchWAVEFilename, "rb");
    if (!waveFile) {
        if (errorDescription) {
            sprintf(errorDescription, "Unable to open wav file\n");
        }
        return 1;
    }
    
    // 创建并初始化amr文件
    amrFile = fopen(pchAMRFileName, "wb");
    if (amrFile == NULL) {
        if (errorDescription) {
            sprintf(errorDescription, "Unable to open amr file\n");
        }
        fclose(waveFile);
        return 1;
    }
    
    /* write magic number to indicate single channel AMR file storage format */
    bytes = fwrite(AMR_MAGIC_NUMBER, sizeof(char), strlen(AMR_MAGIC_NUMBER), amrFile);
    
    /* skip to pcm audio data*/
    skipToPCMAudioData(waveFile);
    
    enstate = Encoder_Interface_init(dtx);
    
    // 此处采用了 只要文件流中的新入数据够一个buffer的容量，就将其读出
    // 若果在没有点击结束录音的情况下，当读取到的size的值小于一个buffer（此处为160）的大小时，就让文件流指针回到读取前的位置，等待新的数据来，够一个buffer后再进行处理
    // 如果用户确定取消了录音，就一直读取数据知道size 值为0。PS：最后的size可能不够一个buffer，但是依然要处理
    while (1) {
        size_t size = ReadPCMFrame(speech, waveFile, channels, bitsPerSample);
        if (isRecord) {
            if (size < 160) {
                fseek(waveFile, size - 160, SEEK_CUR);
            }
            else {
                frames++;
                byte_counter = (size_t) Encoder_Interface_Encode(enstate, req_mode, speech, amrFrame, 0);
                bytes += byte_counter;
                fwrite(amrFrame, sizeof(unsigned char), byte_counter, amrFile);
            }
        }
        else {
            if (size) {
                frames++;
                byte_counter = (size_t) Encoder_Interface_Encode(enstate, req_mode, speech, amrFrame, 0);
                bytes += byte_counter;
                fwrite(amrFrame, sizeof(unsigned char), byte_counter, amrFile);
            }
            else break;
        }
    }
    Encoder_Interface_exit(enstate);
    
    fclose(amrFile);
    fclose(waveFile);
    
    return frames;
}


#pragma mark - Decode


//decode
void WriteWAVEFileHeader(FILE *fpwave, int nFrame) {
    char tag[10] = "";
    
    // 1. 写RIFF头
    RIFFHEADER riff;
    strcpy(tag, "RIFF");
    memcpy(riff.chRiffID, tag, 4);
    riff.nRiffSize = 4                                     // WAVE
    + sizeof(XCHUNKHEADER)               // fmt
    + sizeof(WAVEFORMATX)           // WAVEFORMATX
    + sizeof(XCHUNKHEADER)               // DATA
    + nFrame * 160 * sizeof(short);    //
    strcpy(tag, "WAVE");
    memcpy(riff.chRiffFormat, tag, 4);
    fwrite(&riff, 1, sizeof(RIFFHEADER), fpwave);
    
    // 2. 写FMT块
    XCHUNKHEADER chunk;
    WAVEFORMATX wfx;
    strcpy(tag, "fmt ");
    memcpy(chunk.chChunkID, tag, 4);
    chunk.nChunkSize = sizeof(WAVEFORMATX);
    fwrite(&chunk, 1, sizeof(XCHUNKHEADER), fpwave);
    memset(&wfx, 0, sizeof(WAVEFORMATX));
    wfx.nFormatTag = 1;
    wfx.nChannels = 1; // 单声道
    wfx.nSamplesPerSec = 8000; // 8khz
    wfx.nAvgBytesPerSec = 16000;
    wfx.nBlockAlign = 2;
    wfx.nBitsPerSample = 16; // 16位
    fwrite(&wfx, 1, sizeof(WAVEFORMATX), fpwave);
    
    // 3. 写data块头
    strcpy(tag, "data");
    memcpy(chunk.chChunkID, tag, 4);
    chunk.nChunkSize = nFrame * 160 * sizeof(short);
    fwrite(&chunk, 1, sizeof(XCHUNKHEADER), fpwave);
}

const int _round(const double x) {
    return ((int) (x + 0.5));
}

// 根据帧头计算当前帧大小
int caculateAMRFrameSize(unsigned char frameHeader) {
    int mode;
    int temp1 = 0;
    int temp2 = 0;
    int frameSize;
    
    temp1 = frameHeader;
    
    // 编码方式编号 = 帧头的3-6位
    temp1 &= 0x78; // 0111-1000
    temp1 >>= 3;
    
    mode = amrEncodeMode[temp1];
    
    // 计算amr音频数据帧大小
    // 原理: amr 一帧对应20ms，那么一秒有50帧的音频数据
    temp2 = _round((double) (((double) mode / (double) AMR_FRAME_COUNT_PER_SECOND) / (double) 8));
    
    frameSize = _round((double) temp2 + 0.5);
    return frameSize;
}

// 读第一个帧 - (参考帧)
// 返回值: 0-出错; 1-正确
int ReadAMRFrameFirst(FILE *amrFile, unsigned char frameBuffer[], int *stdFrameSize, unsigned char *stdFrameHeader) {
    memset(frameBuffer, 0, sizeof *frameBuffer);
    
    // 先读帧头
    fread(stdFrameHeader, 1, sizeof(unsigned char), amrFile);
    if (feof(amrFile)) {
        return 0;
    }
    
    // 根据帧头计算帧大小
    *stdFrameSize = caculateAMRFrameSize(*stdFrameHeader);
    
    // 读首帧
    frameBuffer[0] = *stdFrameHeader;
    fread(&(frameBuffer[1]), 1, (*stdFrameSize - 1) * sizeof(unsigned char), amrFile);
    if (feof(amrFile)) return 0;
    
    return 1;
}


int ReadAMRFrame(FILE *amrFile, unsigned char frameBuffer[], size_t stdFrameSize, unsigned char stdFrameHeader) {
    size_t bytes = 0;
    unsigned char frameHeader;
    
    memset(frameBuffer, 0, sizeof *frameBuffer);
    
    // 如果是坏帧(不是标准帧头)，则继续读下一个字节，直到读到标准帧头
    while (1) {
        bytes = fread(&frameHeader, 1, sizeof(unsigned char), amrFile);
        if (feof(amrFile)) {
            return 0;
        }
        if (frameHeader == stdFrameHeader) {
            break;
        }
    }
    
    // 读该帧的语音数据(帧头已经读过)
    frameBuffer[0] = frameHeader;
    bytes = fread(&(frameBuffer[1]), 1, (stdFrameSize - 1) * sizeof(unsigned char), amrFile);
    return !feof(amrFile);
}

int changeStateTo(int isRecording) {
    isRecord = isRecording;
    return 0;
}

int decodeAMRFileToWAVEFile(const char *pchAMRFileName, const char *pchWAVEFilename, char errorDescription[100]) {
    FILE *amrFile = NULL;
    FILE *waveFile = NULL;
    char magic[8];
    void *decodeState;
    int nFrameCount = 0;
    int stdFrameSize;
    unsigned char stdFrameHeader;
    
    unsigned char amrFrame[MAX_AMR_FRAME_SIZE];
    short pcmFrame[PCM_FRAME_SIZE];
    
    // open amr file
    amrFile = fopen(pchAMRFileName, "rb");
    if (!amrFile) {
        if (errorDescription) {
            sprintf(errorDescription, "Unable to open amr file \n");
        }
        return 0;
    }
    
    // check amr file header
    fread(magic, sizeof(char), strlen(AMR_MAGIC_NUMBER), amrFile);
    if (strncmp(magic, AMR_MAGIC_NUMBER, strlen(AMR_MAGIC_NUMBER))) {
        if (errorDescription) {
            snprintf(errorDescription, 10, "Bad header");
        }
        fclose(amrFile);
        return 0;
    }
    
    // open wav file
    waveFile = fopen(pchWAVEFilename, "wb");
    
    if (!waveFile) {
        if (errorDescription) {
            sprintf(errorDescription, "Unable to open wav file\n");
        }
        return 0;
    }
    
    WriteWAVEFileHeader(waveFile, nFrameCount);
    
    /* init decoder */
    decodeState = Decoder_Interface_init();
    
    // 读第一帧 - 作为参考帧
    memset(amrFrame, 0, sizeof(amrFrame));
    memset(pcmFrame, 0, sizeof(pcmFrame));
    ReadAMRFrameFirst(amrFile, amrFrame, &stdFrameSize, &stdFrameHeader);
    
    // 解码一个AMR音频帧成PCM数据
    Decoder_Interface_Decode(decodeState, amrFrame, pcmFrame, 0);
    nFrameCount++;
    fwrite(pcmFrame, sizeof(short), PCM_FRAME_SIZE, waveFile);
    
    // 逐帧解码AMR并写到WAVE文件里
    while (1) {
        memset(amrFrame, 0, sizeof(amrFrame));
        memset(pcmFrame, 0, sizeof(pcmFrame));
        if (!ReadAMRFrame(amrFile, amrFrame, stdFrameSize, stdFrameHeader))
            break;
        
        // 解码一个AMR音频帧成PCM数据 (8k-16b-单声道)
        Decoder_Interface_Decode(decodeState, amrFrame, pcmFrame, 0);
        nFrameCount++;
        fwrite(pcmFrame, sizeof(short), PCM_FRAME_SIZE, waveFile);
    }
    Decoder_Interface_exit(decodeState);
    
    fclose(waveFile);
    
    // 重写WAVE文件头
    waveFile = fopen(pchWAVEFilename, "r+");
    WriteWAVEFileHeader(waveFile, nFrameCount);
    fclose(waveFile);
    
    return nFrameCount;
}